package cn.com.soole.demo.client;

import cn.com.soole.demo.callback.MqttCallBackImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.Random;

/**
 * MQTT配置
 * @date 2024/8/16 09:20
 * @author create by jimmy 2116398125@qq.com
 */
@Slf4j
//@Component
public class StuMqttClient {
    private String username = "test";

    private String password = "test";

    private String hostUrl = "tcp://172.16.30.185:1883";

    /**
     * 客户端对象
     */
    private final IMqttClient client;

    public StuMqttClient(String ip) {
        try {
            Random ran = new Random();
            HashSet hs = new HashSet();
            for(;;) {
                int tmp = ran.nextInt(999999) + 1;
                hs.add(tmp);
                if(hs.size() == 5) break;
            }

            String clientId = ip + StringUtils.join(hs.toArray(), "-");
            //创建MQTT客户端对象
            client = new MqttClient(hostUrl, clientId, new MemoryPersistence());
        } catch (MqttException e) {
            log.error("初始化客户端mqttClient对象失败, errorMsg={}, brokerUrl={}, clientId={}", e.getMessage(), hostUrl, "clientId");
            throw new RuntimeException(e);
        }

        connect();
    }

    /**
     * 客户端连接服务端
     * @date 2024/8/16 09:19
     * @author create by jimmy 2116398125@qq.com
     */
    public void connect() {
        //连接设置
        MqttConnectOptions options = new MqttConnectOptions();
        // 当客户端首次尝试连接broker失败时, 会再次进行尝试
        //options.setAutomaticReconnect(true);

        //是否清空session，设置为false表示服务器会保留客户端的连接记录（订阅主题，qos），客户端重连之后能获取到服务器在客户端断开连接期间推送的消息
        //设置为true表示每次连接到服务端都是以新的身份, 代表临时会话
        options.setCleanSession(true);
        //设置连接用户名
        options.setUserName(username);
        //设置连接密码
        options.setPassword(password.toCharArray());
        //设置超时时间，单位为秒
        options.setConnectionTimeout(100);
        //设置心跳时间 单位为秒，表示服务器每隔1.5*20秒的时间向客户端发送心跳判断客户端是否在线
        options.setKeepAliveInterval(20);

        //设置遗嘱消息的话题，若客户端和服务器之间的连接意外断开，服务器将发布客户端的遗嘱信息
        options.setWill("willTopic", (client.getClientId() + "与服务器断开连接").getBytes(),0,false);

        try {
            if (!client.isConnected()) {
                //设置回调
                client.setCallback(new MqttCallBackImpl(StuMqttClient.this));
                client.connect(options);
            } else {
                client.disconnect();
                client.setCallback(new MqttCallBackImpl(StuMqttClient.this));
                client.connect(options);
            }
            subscribe("msgCount/#", 1);
        } catch (MqttException e) {
            e.printStackTrace();
            log.info("连接mqtt服务器失败");
        }
    }

    /**
     * 断开连接
     * @date 2024/8/16 09:41
     * @author create by jimmy 2116398125@qq.com
     */
    public void disConnect() {
        try {
            client.disconnect();
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

    public void publish(String topic, String message, Integer qos, boolean retained) {
        MqttMessage mqttMessage = new MqttMessage();
        mqttMessage.setQos(qos);
        mqttMessage.setRetained(retained);
        mqttMessage.setPayload(message.getBytes());
        //主题目的地，用于发布/订阅消息
        MqttTopic mqttTopic = client.getTopic(topic);
        //提供一种机制来跟踪消息的传递进度。
        //用于在以非阻塞方式（在后台运行）执行发布时跟踪消息的传递进度
        MqttDeliveryToken token;
        try {
            //将指定消息发布到主题，但不等待消息传递完成。返回的token可用于跟踪消息的传递状态。
            //一旦此方法干净地返回，消息就已被客户端接受发布。当连接可用时，将在后台完成消息传递。
            token = mqttTopic.publish(mqttMessage);
            token.waitForCompletion();
            if (!token.isComplete()) {
                log.info("消息发布未完成, 发布消息失败!");
            }
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }


    /**
     * 订阅主题
     * @date 2024/8/16 09:18
     * @param topic, qos
     * @author create by jimmy 2116398125@qq.com
     */
    public void subscribe(String topic, Integer qos) {
        try {
            client.subscribe(topic, qos);
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

    /**
     * 取消订阅
     * @date 2024/8/15 22:45
     * @param topic, qos
     * @author create by jimmy 2116398125@qq.com
     */
    public void unSubscribe(String topic) {
        try {
            client.unsubscribe(topic);
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }
}
